//
//  ActivityIndicator.swift
//  Signup
//
//  Created by heze on 2018/9/29.
//  Copyright © 2018 apple. All rights reserved.
//

import Foundation
import RxCocoa
import RxSwift

private struct ActivityToken<E> : ObservableConvertibleType, Disposable {
    
    private let _source: Observable<E>
    private let _dispose: Cancelable
    
    init(source: Observable<E>, disposeAction: @escaping () -> ()) {
        
        _source = source
        _dispose = Disposables.create(with: disposeAction)
        
    }
    
    func asObservable() -> Observable<E> {
        return _source
    }
    
    func dispose() {
        _dispose.dispose()
    }
    
    
}

public class ActivityIndicator: SharedSequenceConvertibleType {
    
    public typealias E = Bool
    public typealias SharingStrategy = DriverSharingStrategy
    
    private let _lock = NSRecursiveLock()
    private let _relay = BehaviorRelay(value: 0)
    private let _loading: SharedSequence<SharingStrategy, Bool>
    
    public init() {
        
        _loading = _relay.asDriver()
            .map{ $0 > 0 }
            .distinctUntilChanged()
    }
    
    fileprivate func trackActivityOfObservable<O: ObservableConvertibleType>(_ source: O) -> Observable<O.E> {
        
        return Observable.using({ () -> ActivityToken<O.E> in
            self.increment()
            
            return ActivityToken(source: source.asObservable(), disposeAction: self.decrement)
            
        }) { t in
            
            return t.asObservable()
        }
    }
    
    private func increment() {
        
        _lock.lock()
        _relay.accept(_relay.value + 1)
        _lock.unlock()
        
    }
    
    private func decrement() {
        _lock.lock()
        _relay.accept(_relay.value - 1)
        _lock.unlock()
    }
    
    public func asSharedSequence() -> SharedSequence<DriverSharingStrategy, Bool> {
        return _loading
    }
    
}


extension ObservableConvertibleType {
    
    public func trackActivity(_ activityIndicator: ActivityIndicator) -> Observable<E> {
        
        return activityIndicator.trackActivityOfObservable(self)
    }
}
